// $Id: request.c 96 2011-10-20 16:24:38Z cedric.shih@gmail.com $
/*
 * Copyright 2011 Cedric Shih (cedric dot shih at gmail dot com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <event.h>

#include "log.h"
#include "headers.h"

#include "request.h"

struct niortsp_request *niortsp_request_new(struct zcbuffer *buffer)
{
	struct niortsp_request *req;

	niortsp_debug("constructing");

	if (!(req = calloc(1, sizeof(*req)))) {
		niortsp_error("failed to calloc");
		goto error;
	}

	if (!(req->input_headers = niortsp_headers_new())) {
		niortsp_error("failed to create input headers");
		goto error;
	}

	if (!(req->output_headers = niortsp_headers_new())) {
		niortsp_error("failed to create output headers");
		goto error;
	}

	req->buffer = buffer;
	return req;

error:
	niortsp_request_free(req);
	return NULL;
}

void niortsp_request_free(struct niortsp_request *req)
{
	if (!req) {
		return;
	}

	niortsp_debug("destructing");

	if (req->protocol) {
		free(req->protocol);
	}

	if (req->version.string) {
		free(req->version.string);
	}

	if (req->method) {
		free(req->method);
	}

	if (req->uri) {
		free(req->uri);
	}

	if (req->input_headers) {
		niortsp_headers_free(req->input_headers);
	}

	if (req->output_headers) {
		niortsp_headers_free(req->output_headers);
	}

	free(req);
}

inline unsigned int niortsp_request_get_major(
		struct niortsp_request *req)
{
	return req->version.major;
}

inline const char *niortsp_request_get_protocol(
		struct niortsp_request *req)
{
	return req->protocol;
}

inline unsigned int niortsp_request_get_minor(
		struct niortsp_request *req)
{
	return req->version.minor;
}

inline const char *niortsp_request_get_method(
		struct niortsp_request *req)
{
	return req->method;
}

inline const char *niortsp_request_get_uri(
		struct niortsp_request *req)
{
	return req->uri;
}

inline const char *niortsp_request_get_input_header(
		struct niortsp_request *req,
		const char *name)
{
	return niortsp_headers_get(req->input_headers, name);
}

inline const char *niortsp_request_get_output_header(
		struct niortsp_request *req,
		const char *name)
{
	return niortsp_headers_get(req->output_headers, name);
}

inline int niortsp_request_set_input_header(
		struct niortsp_request *req,
		const char *name, const char *value)
{
	return niortsp_headers_set(req->input_headers, name, value);
}

inline int niortsp_request_set_output_header(
		struct niortsp_request *req,
		const char *name, const char *value)
{
	return niortsp_headers_set(req->output_headers, name, value);
}

struct evbuffer;

inline struct zcbuffer *niortsp_request_get_buffer(struct niortsp_request *req)
{
	return req->buffer;
}

inline int niortsp_request_set_protocol(struct niortsp_request *req,
		const char *protocol)
{
	return niortsp_request_nset_protocol(req, protocol, strlen(protocol));
}

int niortsp_request_nset_protocol(struct niortsp_request *req,
		const char *protocol, size_t len)
{
	int rc;
	char *str;

	if (!req || !protocol) {
		niortsp_error("null argument(s)");
		rc = EINVAL;
		goto finally;
	}

	niortsp_debug("setting protocol: %.*s", len, protocol);

	if (!(str = malloc(len + 1))) {
		niortsp_error("failed to malloc");
		rc = ENOMEM;
		goto finally;
	}

	if (req->protocol) {
		free(req->protocol);
	}

	req->protocol = str;
	memcpy(req->protocol, protocol, len);
	req->protocol[len] = '\0';
	rc = 0;

finally:
	return rc;
}

inline const char *niortsp_request_version(struct niortsp_request *req)
{
	return req->version.string;
}

inline int niortsp_request_major_version(struct niortsp_request *req)
{
	return req->version.major;
}

inline int niortsp_request_minor_version(struct niortsp_request *req)
{
	return req->version.minor;
}

inline int niortsp_request_set_version(struct niortsp_request *req,
		const char *version)
{
	return niortsp_request_nset_version(req, version, strlen(version));
}

int niortsp_request_nset_version(struct niortsp_request *req,
		const char *string, size_t len)
{
	int rc;
	char *str = NULL, *ptr;
	long value;

	if (!req || !string) {
		niortsp_error("null argument(s)");
		rc = EINVAL;
		goto finally;
	}

	niortsp_debug("setting version: %.*s", len, string);

	if (!(str = malloc(len + 1))) {
		niortsp_error("failed to malloc");
		rc = ENOMEM;
		goto finally;
	}

	memcpy(str, string, len);
	str[len] = '\0';

	value = strtol(str, &ptr, 10);

	if (ptr == str || value < 0 || *ptr != '.' ||
			*(ptr + 1) == '\0') {
		niortsp_error("invalid version: %s", str);
		rc = EINVAL;
		goto finally;
	}

	req->version.major = value;

	value = strtol(ptr + 1, &ptr, 10);

	if (value < 0 || ptr != (str + len)) {
		rc = EINVAL;
		niortsp_error("invalid version: %s", str);
		goto finally;
	}

	req->version.minor = value;

	if (req->version.string) {
		free(req->version.string);
	}

	req->version.string = str;
	rc = 0;

finally:
	if (rc != 0) {
		if (str) {
			free(str);
		}
	}
	return rc;
}

inline const char *niortsp_request_method(struct niortsp_request *req)
{
	return req->method;
}

inline int niortsp_request_set_method(struct niortsp_request *req,
		const char *method)
{
	return niortsp_request_nset_method(req, method, strlen(method));
}

int niortsp_request_nset_method(struct niortsp_request *req,
		const char *method, size_t len)
{
	int rc;
	char *str;

	if (!req || !method) {
		niortsp_error("null argument(s)");
		rc = EINVAL;
		goto finally;
	}

	niortsp_debug("setting method: %.*s", len, method);

	if (!(str = malloc(len + 1))) {
		niortsp_error("failed to malloc");
		rc = ENOMEM;
		goto finally;
	}

	if (req->method) {
		free(req->method);
	}

	req->method = str;
	memcpy(req->method, method, len);
	req->method[len] = '\0';
	rc = 0;

finally:
	return rc;
}

inline const char *niortsp_request_uri(struct niortsp_request *req)
{
	return req->uri;
}

inline int niortsp_request_set_uri(struct niortsp_request *req,
		const char *uri)
{
	return niortsp_request_nset_uri(req, uri, strlen(uri));
}

int niortsp_request_nset_uri(struct niortsp_request *req,
		const char *uri, size_t len)
{
	int rc;
	char *str;

	if (!req || !uri) {
		niortsp_error("null argument(s)");
		rc = EINVAL;
		goto finally;
	}

	niortsp_debug("setting URI: %.*s", len, uri);

	if (!(str = malloc(len + 1))) {
		niortsp_error("failed to malloc");
		rc = ENOMEM;
		goto finally;
	}

	if (req->uri) {
		free(req->uri);
	}

	req->uri = str;
	memcpy(req->uri, uri, len);
	req->uri[len] = '\0';
	rc = 0;

finally:
	return rc;
}
